﻿using System.Composition;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Windows.Storage;

namespace System.Services
{
    public interface IStrorageManager
    {
        Task<T> RetrieveAsync<T>(StorageFile file, DataContractSerializerSettings settings);
        Task<T> RetrieveAsync<T>(StorageFolder folder, string name, DataContractSerializerSettings settings);
        Task StoreAsync<T>(StorageFile file, T value, DataContractSerializerSettings settings);
        Task StoreAsync<T>(StorageFolder folder, string name, T value, DataContractSerializerSettings settings);

        Task<T> RetrieveAsync<T>(StorageFile file);
        Task<T> RetrieveAsync<T>(StorageFolder folder, string name);
        Task StoreAsync<T>(StorageFile file, T value);
        Task StoreAsync<T>(StorageFolder folder, string name, T value);
    }

    [Shared]
    [Export(typeof(IStrorageManager))]
    //[ExportService("StorageService", "IO Storage service", typeof(IStrorageManager), 40)]
    public class StorageManager : IStrorageManager
    {
        public StorageManager()
        {
        }

        public async Task<T> RetrieveAsync<T>(StorageFile file, DataContractSerializerSettings settings)
        {
            // Validate parameters

            if (file == null)
                throw new ArgumentNullException("file");

            // Open the file from the file stream

            using (Stream fileStream = await file.OpenStreamForReadAsync())
            {
                // Copy the file to a MemoryStream (as we can do this async)
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    await fileStream.CopyToAsync(memoryStream);
                    memoryStream.Seek(0, SeekOrigin.Begin);

                    try
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(T), settings);
                        return (T)serializer.ReadObject(memoryStream);
                    }
                    catch (Exception exc)
                    {
                    }
                }
            }
            return default(T);
        }

        public async Task<T> RetrieveAsync<T>(StorageFolder folder, string name, DataContractSerializerSettings settings)
        {
            // Validate parameters

            if (folder == null)
                throw new ArgumentNullException("folder");

            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            // Open the file, if it doesn't exist then return default, otherwise pass on
            // NB : Currently the way to check if a file exists in WinRT is by catching the exception
            // TODO : Check with Windows 8 RTM whether GetFileAsync(...) method returns null if a file doesn't exist or another method allows checking of this

            try
            {
                StorageFile file = await folder.GetFileAsync(name);
                return await RetrieveAsync<T>(file, settings);
            }
            catch (FileNotFoundException e)
            {
                return default(T);
            }
        }

        public async Task StoreAsync<T>(StorageFile file, T value, DataContractSerializerSettings settings)
        {

            if (file == null)
                throw new ArgumentNullException("file");

            // Write the object to a MemoryStream using the DataContractSerializer
            // NB: Do this so that,
            //        (i)  We store the state of the object at this point in case it changes before we open the file
            //        (ii) DataContractSerializer doesn't provide async methods for writing to storage
            // TODO : Alternatively we could perform this directly on the file stream and call 'await fileStream.FlushAsync()' (will this ever block?)

            try
            {
                using (MemoryStream dataStream = new MemoryStream())
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof(T), settings);
                    serializer.WriteObject(dataStream, value);

                    // Save the data to the file stream

                    using (Stream fileStream = await file.OpenStreamForWriteAsync())
                    {
                        dataStream.Seek(0, SeekOrigin.Begin);
                        await dataStream.CopyToAsync(fileStream);
                    }
                }
            }
            catch (Exception exc)
            {
            }

        }

        public async Task StoreAsync<T>(StorageFolder folder, string name, T value, DataContractSerializerSettings settings)
        {
            // Validate parameters

            if (folder == null)
                throw new ArgumentNullException("folder");

            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            // Create the new file, overwriting the existing data, then pass on

            StorageFile file = await folder.CreateFileAsync(name, CreationCollisionOption.ReplaceExisting);
            await StoreAsync<T>(file, value, settings);
        }

        public Task<T> RetrieveAsync<T>(StorageFile file)
        {
            return this.RetrieveAsync<T>(file, new DataContractSerializerSettings());
        }

        public Task<T> RetrieveAsync<T>(StorageFolder folder, string name)
        {
            return RetrieveAsync<T>(folder, name, new DataContractSerializerSettings());
        }

        public async Task StoreAsync<T>(StorageFile file, T value)
        {
            await StoreAsync<T>(file, value, new DataContractSerializerSettings());
        }

        public async Task StoreAsync<T>(StorageFolder folder, string name, T value)
        {
            await StoreAsync<T>(folder, name, value, new DataContractSerializerSettings());
        }


        public void StartService()
        {
        }

        public void StopService()
        {
        }

        public void Dispose()
        {
        }
        //[ImportMany(typeof(IPersistedObject))]
        //public ObservableCollection<Lazy<IPersistedObject, IPersistedObjectMetadata>> PersistedObjects { get; set; }
    }
}
